// transferFunction.h
// This is a design and implementation of perhaps the simplest transfer function

#ifndef TRANSFERFUNCTION_H
#define TRANSFERFUNCTION_H

#include "model.h"

//#include "drawHistogram.h"

namespace visualization
{
// Description:
// 1. The program first gives a diagram depicting the histogram of 
//    the data field, and then the user is asked to determine the
//    threshold needed for data classification; the coodinates of
//    of the axis of the diagram should be between 0 and 1 for ea-
//    se of handling, so is the thresholds given by users;
class visTransferFunction
{
public:
	visTransferFunction()
	{
		lookup_table = 0;
//		from_liup = 0;
	}
	visTransferFunction(Model *mdl)
	{
		data_field = mdl;
		if(! mdl->getDataValueRange(dataLow, dataHigh) )
		{
			exit(-1);
		}
		lookup_table = 0;
//		from_liup = 0;
	}
	~visTransferFunction()
	{

		if(lookup_table)
		{
			for(int i = 0; i < thhNum; i++)
			{
				delete []lookup_table[i];
			}
			delete []lookup_table;
		}
	}
	void setDataField(Model *mdl)
	{
		data_field = mdl;
		if(! mdl->getDataValueRange(dataLow, dataHigh) )
		{
			exit(-1);
		}
	}
	
	// This function receives several thresholds and use them to classify the 
	// data set. After that, the lookup table is established.
	void inputThresholds()
	{
		cout << endl << "-------------------How many thresholds have you choozen?---------------------- " << endl
		     << "Not that you have n threshold values if you have n+1 classes of materials!" << endl;
		int tmp;
		cin >> tmp;

		// Here, note that one more item are set.
		thhNum = tmp + 1;
		lookup_table = new double*[thhNum];
		// Mallocate memory for the variable lookup_table.
		for(int i = 0; i < thhNum; i++)
		{
			lookup_table[i] = new double[one_item_num];
		}
		// The last threshold is set to 1.
		lookup_table[tmp][0] = 1;

		int i = 0;
		if(thhNum > 0)
		{
			cout << endl << "Now tell me them please: -------------------------------" << endl;
		}
		while(i < tmp)
		{
			cout << endl << "--The " << i << "th you give is: " << endl;
			cin >> lookup_table[i][0];
			if(1 < lookup_table[i][0] || 0 > lookup_table[i][0])
			{
				cout << endl << "!!!!Note that the value should fall in between 0 and 1. But you"
				     << "have input an invalid number. Reinput please!!!!" << endl;				         
				continue;
			}
			i++;
		}
		// Set color and opacity value for each class of material.
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!Seems that this transfer function is designed in a very naive way!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
		for(int i = 0; i < thhNum; i++)
		{
			// RGB
			lookup_table[i][1] = 100*(1.2-i/(double)thhNum);
			lookup_table[i][2] = 150*(1.2-i/(double)thhNum*0.05);
			lookup_table[i][3] = 200*(1.2-i/(double)thhNum*0.01);
			// Opacity
			//lookup_table[i][4] = i/(double)(thhNum * 10);
			lookup_table[i][4] = 1 - i/(double)thhNum;
		}
	}
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	// This function receives several thresholds and use them to classify the 
	// data set. After that, the lookup table is established.
	void inputThresholdsByValue()
	{
		cout << endl << "-------------------How many thresholds have you choozen?---------------------- " << endl
		     << "Not that you have n threshold values if you have n+1 classes of materials!" << endl;
		int tmp;
		cin >> tmp;

		// Here, note that one more item are set.
		thhNum = tmp + 1;
		lookup_table = new double*[thhNum];
		// Mallocate memory for the variable lookup_table.
		for(int i = 0; i < thhNum; i++)
		{
			lookup_table[i] = new double[one_item_num];
		}
		// The last threshold is set to 1.
		lookup_table[tmp][0] = 1;

		int i = 0;
		double th;
		cout << endl << "Now tell me them please: -------------------------------" << endl;
		while(i < tmp)
		{
			cout << endl << "--The " << i << "th you give is: " << endl;
			cin >> th;
			lookup_table[i][0] = (th - dataLow)/(dataHigh - dataLow);
			i++;
		}

		// Set color and opacity value for each class of material.
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!Seems that this transfer function is designed in a very naive way!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
		for(int i = 0; i < thhNum; i++)
		{
			// RGB
/*			lookup_table[i][1] = 100*(1.2-i/(double)thhNum);
			lookup_table[i][2] = 150*(1.2-i/(double)thhNum*0.05);
			lookup_table[i][3] = 200*(1.2-i/(double)thhNum*0.01);
*/			// Opacity
			lookup_table[i][1] = 255*i/(double)thhNum;
			lookup_table[i][2] = 0.5*255*i/(double)thhNum;
			lookup_table[i][3] = 255 - i/(double)thhNum*100;
			//lookup_table[i][4] = i/(double)(thhNum * 10);
			lookup_table[i][4] = (0.5+i)/(double)thhNum;

/*
switch(i)
{
	case 0:
			lookup_table[i][1] = 200;
			lookup_table[i][2] = 10;
			lookup_table[i][3] = 60;
			lookup_table[i][4] = 0.2;
	case 1:
			lookup_table[i][1] = 150;
			lookup_table[i][2] = 100;
			lookup_table[i][3] = 10;
			lookup_table[i][4] = 0.2;
	case 2:
			lookup_table[i][1] = 100;			
			lookup_table[i][2] = 150;
			lookup_table[i][3] = 240;
			lookup_table[i][4] = 0.3;
	case 3:	
			lookup_table[i][1] = 50;
			lookup_table[i][2] = 155;
			lookup_table[i][3] = 220;
			lookup_table[i][4] = 0.1;
	case 4:	
			lookup_table[i][1] = 100;
			lookup_table[i][2] = 200;
			lookup_table[i][3] = 150;
			lookup_table[i][4] = 0.3;
	case 5:	
			lookup_table[i][1] = 40;
			lookup_table[i][2] = 120;
			//lookup_table[i][4] = i/(double)(thhNum * 10);
			lookup_table[i][3] = 200;
			lookup_table[i][4] = 0.2;
	case 6:	
			lookup_table[i][1] = 200;
			lookup_table[i][2] = 100;
			lookup_table[i][3] = 20;
			lookup_table[i][4] = 0.4;
			//lookup_table[i][4] = i/(double)(thhNum * 10);
	case 7:	
			lookup_table[i][4] = 244;			
			lookup_table[i][2] = 120;
			lookup_table[i][3] = 10;
			//lookup_table[i][4] = i/(double)(thhNum * 10);
			lookup_table[i][4] = 0.1;
}//switch
*/
	}
	}
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

	
	// This is the only function used for output. When a value is given, the 
	// function searches the lookup talbe to find a index of an item in whose
	// range the value falls. Certainly, the returned index is the upbound de-
	// scribed below for the lookup_table. 
	// If succeds, a number between 0 and 1 (including 1) is return, otherwise
	// -1 is returned.
	int getRGBIndex(double value)
	{
		if(value > dataHigh || value < dataLow)
		{
			cout << "AT LINE " << __LINE__ << "OF FILE " << __FILE__ << endl;
			cout << "Out of data range! Check the program please!" << endl;
			return -1;
		}
		value = (value - dataLow) / (dataHigh - dataLow);
		int low = 0, high = thhNum - 1;
		int med;
		while(low <= high)
		{
			med = (high + low) / 2;
			if(value == lookup_table[med][0])
			{
				break;
			}
			if(value < lookup_table[med][0]) high = med - 1;
			else low = med + 1;
		}
/*		if((value > lookup_table[med][0] || med == 0) && med != thhNum - 1)
		{med++;}
*/		if(med != 0 && med != thhNum -1)
		{
			if(value < (lookup_table[med][0] + lookup_table[med-1][0])/2)
				med--;
			if(value > (lookup_table[med][0] + lookup_table[med+1][0])/2)
				med++;
		}
		return med;
	}

	double* forABCD(double rate)
	{
		if(rate < 0.02)
		{	
/*			    from_liup[0] = 150;
				from_liup[1] = 200;
				from_liup[2] = 255;
				from_liup[3] = rate * 5;
*/
				from_liup[0] = 255 * (0.03-rate)/0.03;
				from_liup[1] = 0;
				from_liup[2] = 0;
				from_liup[3] = rate * 5;
		}
		else if(rate < 0.05)
		{
			from_liup[0] = 0;
			from_liup[1] = 255 * exp( -pow(rate-0.05, 2)/0.045);
			from_liup[2] = 0;
			from_liup[3] = rate * 2;
		}
		else if(rate < 0.07)
		{
				from_liup[0] = 255;
				from_liup[1] = 255 * (0.1-rate)/0.1;
				from_liup[2] = 0;
				//from_liup[3] = rate * 4;
				from_liup[3] = rate * 2;
		}
		else if(rate < 0.3)
		{
				from_liup[0] = 0;
				from_liup[1] = 0;
				from_liup[2] = 255 * exp( -pow(rate - 0.13, 2)/0.225 );
				from_liup[3] = rate;
		}
		else
		{
				from_liup[0] = 255;
				from_liup[1] = 255 * exp( -pow(1-rate, 2) );
				from_liup[2] = 255;
				//from_liup[3] = rate / 2;
				from_liup[3] = rate / 5;
		}
		return from_liup;
	}
	double *forCTAPERT(double rate)
	{
		if(rate < 0.1)
		{	
			from_liup[0] = 100 * (0.03-rate)/0.03;
			from_liup[1] = 0;
			from_liup[2] = 0;
			from_liup[3] = 0;
		}
		else if(rate < 0.3)
		{
			from_liup[0] = 200;
			from_liup[1] = 100;
			from_liup[2] = 255 * (0.4 - rate)/0.5;
			from_liup[3] = rate/2;
		}
/*		else if(rate < 0.4)
		{
			from_liup[0] = 
		}
*/		else if(rate < 0.55)
		{
			// Important.
			from_liup[0] = 200;
			from_liup[1] = 255*(0.55 - rate)/0.55;
			from_liup[2] = 255;
			from_liup[3] = rate / 3;
		}
		else if(rate < 0.7)
		{
			from_liup[0] = 250 * (0.7 - rate)/0.7;
			from_liup[1] = 200;
			from_liup[2] = 150;
			from_liup[3] = rate/6;
		}
		else
		{
			from_liup[0] = 255;
			from_liup[1] = 255;
			from_liup[2] = 255 * (1 - rate);
			from_liup[3] = rate/15;
		}
		return from_liup;
	}
	double *forFrog(double rate)
	{
		if(rate < 0.3)
		{	
			from_liup[0] = 100 * (0.03-rate)/0.03;
			from_liup[1] = 0;
			from_liup[2] = 0;
			from_liup[3] = rate/100;
		}
		else if(rate < 0.38)
		{
			from_liup[0] = 200;
			from_liup[1] = 100;
			from_liup[2] = 255 * (0.4 - rate)/0.5;
			from_liup[3] = rate/10;
		}
		else if(rate < 0.46)
		{
			from_liup[0] = 200 * (0.46-rate);
			from_liup[1] = 255 * (0.5 - rate);
			from_liup[2] = 100 * (0.5 - rate);
			from_liup[3] = rate/10;
		}
		else if(rate < 0.55)
		{
			// Important.
			from_liup[0] = 200;
			from_liup[1] = 255*(0.55 - rate)/0.55;
			from_liup[2] = 255;
			from_liup[3] = rate/10;
		}
		else if(rate < 0.65)
		{
			from_liup[0] = 0;
			from_liup[1] = 200;
			from_liup[0] = 100;
			from_liup[0] = rate/10;
		}
		else if(rate < 0.75)
		{
			from_liup[0] = 250 * (0.7 - rate)/0.7;
			from_liup[1] = 200;
			from_liup[2] = 150;
			from_liup[3] = rate/10;
		}
		else if(rate < 0.85)
		{
			from_liup[0] = 150;
			from_liup[1] = 200 * (1 - rate);
			from_liup[2] = 255;
			from_liup[3] = rate/10;
		}
		else
		{
			from_liup[0] = 255;
			from_liup[1] = 255;
			from_liup[2] = 255 * (1 - rate);
			from_liup[3] = rate/100;
		}
		return from_liup;
	}

	double *forvisMale(double rate)
	{
		if(rate < 0.08)
		{
			from_liup[0] = 255;
			from_liup[1] = 255;
			from_liup[2] = 255;// * exp( -pow(rate - 0.05, 2)/0.006 );
			from_liup[3] = rate/10;
		}
		else if(rate < 0.2)
		{
			//double tmp = exp( -pow(rate - 0.12, 2)/0.006 );

			from_liup[0] = 255 * ( 1 - rate );
			from_liup[1] = 255 * rate;
			from_liup[2] = 0;
			from_liup[3] = rate/1.2;
		}
		else if(rate < 0.5)
		{
			//double tmp = exp( -pow(rate - 0.42, 2)/0.006 );
			from_liup[0] = 220 * (1 - rate);
			from_liup[1] = 120;
			from_liup[2] = 250 * rate;
			from_liup[3] = rate/3;
		}		
		else if(rate < 0.65)
		{
			from_liup[0] = 70;
			from_liup[1] = 255 * exp( -pow(rate - 0.45, 2)/0.09 );
			from_liup[2] = 70;
			from_liup[3] = rate/5;
		}
		else
		{
			from_liup[0] = 0;
			from_liup[1] = 150 * rate;
			from_liup[2] = 120 * (1 - rate);
			from_liup[3] = rate/10;
		}
		return from_liup;
	}

	double *forLoster(double rate)
	{
		if(rate < 0.1)
		{
			from_liup[0] = 0;//255;
			from_liup[1] = 0;//255;
			from_liup[2] = 0;//255 * exp( -pow(rate - 0.08, 2)/0.006 );
			from_liup[3] = 0;
		}
		else if(rate < 0.2)
		{
			from_liup[0] = 0;
			from_liup[1] = 100;
			from_liup[2] = 255 * exp( -pow(rate - 0.2, 2)/0.1 );
			from_liup[3] = rate/2.5;
		}
		else if(rate < 0.5)
		{
			from_liup[0] = 220 * (rate - 0.15)/0.3;
			from_liup[1] = 100;
			from_liup[2] = 0;
			from_liup[3] = rate/4.5;
		}
		else if(rate < 0.6)
		{
			from_liup[0] = 70;
			from_liup[1] = 255 * exp( -pow(rate - 0.55, 2)/0.006 );//* exp( -pow(rate - 1, 2)/0.6 );
			from_liup[2] = 70;
			from_liup[3] = rate/4;
		}
		else
		{
			from_liup[0] = 0;
			from_liup[1] = 0;
			from_liup[2] = 220 * (1 - rate)/0.5;
			from_liup[3] = rate/10;
		}
		return from_liup;
	}

	// Access the corresponding color and opacity value by index. The returned pointer points 
	// to the RGB color and the opacity. Note that the returned pointer points to an array of 
	// size 4.
	double *getColorAndOpacity(double value)
	{
/*		int index = getRGBIndex(value);
		return lookup_table[index] + 1;
*/
		double rate = (value - dataLow)/dataHigh;

//		return forABCD(rate);
		return forFrog(rate);
		//return forCTAPERT(rate);
	//	return forvisMale(rate);
		//return forLoster(rate);
	}
private:
	Model *data_field;
// The structure of the lookup table is like this:
//  1.the range of the data value is devided into several segments(by the variable...);
//  2.one row of the table is one item, which is one segment of the range;
//  3.one item consists of a threshold, the color(3 elements: RGB) and the opacity value;
//    that is, there are five numbers in each row;
//  4.the item records the upbound of a range, for example, the range is 1 ~ 10, and is 
//    devided into 9 segments, then the upbound of the first item is 2, and the second 3,
//    and so on. The last, of cource, is 10.
//  5.The thresholds saved are "wave valley", so the begging and tail of the curve may not be 
//    saved. But for convenience of indexing, they should be included. Therefor, two additional
//    items are set when allocating memory for thresholds dynamically.
	int thhNum;
	static const int one_item_num = 5;
	double **lookup_table;

	double dataLow, dataHigh;

	double from_liup[4];
};
}
#endif

